package net.gdface.facelog;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;

import net.gdface.utils.NetworkUtil;

/**
 * 本地服务进程控制器基类
 * 如果服务指定为本机服务，则尝试连接，连接不上则尝试启动
 * @author guyadong
 *
 */
public abstract class BaseServiceController implements ServiceConstant{
	protected static final long DEFAULT_TRY_INTERVAL = 2000L;
	protected static final int DEFAULT_TRY_COUNT = 10;
	protected volatile Object embeddedObject;
	protected volatile Process process;
	protected URI location;
	/** 当服务不存在时是否等待 */
	protected boolean waitIfAbsent = false;
	/** 尝试连接服务的次数 */
	protected int tryCountLimit = DEFAULT_TRY_COUNT;
	/** 尝试连接服务的时间间隔(毫秒) */
	protected long tryInterval = DEFAULT_TRY_COUNT;
	protected BaseServiceController() {
		/** 程序结束时关闭服务 */
		Runtime.getRuntime().addShutdownHook(new Thread(){
			@Override
			public void run() {
				try {
					if(isEmbedded()){
						shutdownEmbeddedServer();
					}else{
						shutdownLocalServer();
					}
				} catch (Exception e) {
					logger.error(e.getMessage(),e);
				}
			}
		});
	}
	/**
	 * 测试服务是否可连接
	 * @return 可连接返回{@code true},否则返回{@code false}
	 */
	abstract protected boolean testConnect();
	/**
	 * 根据配置文件返回是否有启动本地服务的条件
	 * @return 可以启动本地服务返回{@code true},否则返回{@code false}
	 */
	abstract protected boolean canStartLocal();
	/**
	 * 本地服务是否为嵌入服务
	 */
	abstract protected boolean isEmbedded();
	/** 
	 * 启动本地(独立进程)服务<br>
	 * {@link #isEmbedded()}返回{@code false}时子类必须重写此方法
	 * @return 服务进程对象
	 */
	protected Process startLocalServer(){
		throw new UnsupportedOperationException();
	}
	/** 
	 * 启动本地嵌入服务<br>
	 * {@link #isEmbedded()}返回{@code true}时子类必须重写此方法
	 * @return 启动嵌入式服务对象后返回的对象,如果启动的嵌入式服务不需要显式执行关闭,则返回{@code null}
	 */
	protected Object startEmbeddedServer(){
		throw new UnsupportedOperationException();	
	}
	/** 
	 * 服务连接初始化,并测试连接,如果连接异常,则尝试启动本地服务器或等待服务启动 
	 * @return 连接参数和工厂类实例是否更新
	 */
	boolean  init(){

		int tryCount = this.tryCountLimit;
		boolean selfBind = NetworkUtil.selfBind(location.getHost());
		String name = serviceName();
		do{
			// 测试服务器连接
			if(testConnect()){
				return false; 
			}

			if(selfBind){
				// 如果 服务指定为本地启动则尝试启动服务器
				if( canStartLocal() ){
					if(isEmbedded()){
						embeddedObject = startEmbeddedServer();
						// 嵌入式服务启动成功后不需要调用testConnect
						return false;
					}else{
						process = startLocalServer();
					}
					for(int c=0;c<tryCount;++c){
						try {
							Thread.sleep(tryInterval);
							// 如果是嵌入式服务则直接通过调用testConnect()来检查是否服务启动正常
							if(isEmbedded()){
								if(testConnect()){
									return false;
								}
							}else{
								// 如果是非嵌入式服务则先调用 process.exitValue() 测试启动服务的命令是否执行完
								// 命令执行完后再调用testConnect()来检查是否服务启动正常
								if(process !=null){
									try {
										process.exitValue();
										if(testConnect()){
											return false;
										}
										break;
									} catch (IllegalThreadStateException itse) {
										// 服务已启动状态下测试连接
										if(testConnect()){
											return false;
										}
									}
								}else{
									continue;
								}
							}
						} catch (InterruptedException ie) {
							break;
						}
					}
					throw new IllegalStateException(String.format("FAIL TO START %s SERVER(启动%s失败) ",name,name));
				}else {
					throw new RuntimeException(
							String.format("cann't connect %s server(无法连接%s服务器,请检查服务器是否启动以及密码是否正确) %s",name,name,location));
				}
			} else if(waitIfAbsent && tryCount-- > 0){
				logger.info("waiting for activemq server...{}",tryCount);
			} else {
				throw new RuntimeException(String.format("cann't connect %s server(无法连接%s服务器) %s",name,name,location));
			}				
		
			try {
				Thread.sleep(tryInterval);
			} catch (InterruptedException e) {
				break;
			}
		}while(tryCount >0);
		throw new RuntimeException(String.format("cann't connect %s server(无法连接%s服务器) %s",name,name,location));
	
	}
	/**
	 * 返回服务名,默认使用当前类名,类名以"Controller"结尾时去掉此结尾转小写为服务名
	 * @return 服务名
	 */
	protected String serviceName(){
		String name = getClass().getSimpleName();
		if(name.endsWith("Controller")){
			return name.substring(0, name.lastIndexOf("Controller") ).toLowerCase();
		}
		return name;
	}
	/**
	 * 尝试查找{@code closeMethodNames}列表指定的close方法,如果有则执行,如果没有则输出警告信息
	 * @param closeMethodNames
	 * @return 成功执行close方法则返回{@code true},否则返回{@code false}
	 */
	private boolean runCloseMethod(String ...closeMethodNames){
		for(String name:closeMethodNames){
			try {
				embeddedObject.getClass().getMethod(name).invoke(embeddedObject);
				embeddedObject = null;
				return true;
			} catch (NoSuchMethodException e) {
				continue;
			} catch (IllegalAccessException | SecurityException e) {
				e.printStackTrace();
				return false;
			}catch (InvocationTargetException e) {
				e.getTargetException().printStackTrace();
				return false;
			}
		}
		return false;
	}
	/** 
	 * 中止本地嵌入式服务<br>
	 * 如果{@link #embeddedObject}为{@link AutoCloseable}实例,则执行close方法完成关闭,
	 * 否则尝试查找{@code close()}方法,如果有则执行,如果没有则输出警告信息后返回<br>
	 * 子类根据需要重写此方法
	 */
	protected void  shutdownEmbeddedServer(){
		if(embeddedObject != null){
			synchronized (this) {
				if(embeddedObject != null){
					if(embeddedObject instanceof AutoCloseable){
						// 如果{@link #embeddedObject}为{@link AutoCloseable}实例,则执行close方法完成关闭
						try {
							((AutoCloseable)embeddedObject).close();
							String name = serviceName();
							logger.info("shutdown {} server(关闭{}服务)",name,name);
							embeddedObject = null;
						} catch (Exception e) {
							e.printStackTrace();
						}
					}else{
						// 尝试查找close 方法,如果有则执行,如果没有则输出警告信息
						if(!runCloseMethod("close","stop")){
							logger.warn("CAN'T run shutdown embedded server,please override this method to immplement your own logic");
						}
					}
				}				
			}
		}
	}
	/** 
	 * 中止本地服务<br>
	 * 子类根据需要重写此方法
	 */
	protected void  shutdownLocalServer(){
		if(process != null){
			synchronized (this) {
				if(process != null){
					try {
						process.exitValue();
					} catch (IllegalThreadStateException  e) {
						String name = serviceName();
						logger.info("shutdown {} server(关闭{}服务)",name,name);
						process.destroy();
					}
					process = null;
				}				
			}
		}
	}
}
